package com.paziy.notes.core.sounds;

import com.paziy.notes.core.Octave;
import com.paziy.notes.core.notes.Accidental;
import com.paziy.notes.core.notes.Note;

/**
 * The class represents sound of note.
 * With note, accidental and octave.
 * Can be used for music notation based on notes (piano, violin and etc)
 * @see com.paziy.notes.core.notes.Note
 * @see com.paziy.notes.core.notes.Accidental
 * @see com.paziy.notes.core.Octave
 *
 * @author thecederick@gmail.com (Paziy Evgeniy)
 * @version 0.2
 * @since 0.1 (03.12.13 9:46)
 */
public final class NoteSound implements Sound, Comparable<NoteSound> {

    /**
     * The default value of note for the default constructor
     */
    private static final Note DEFAULT_NOTE = Note.C;

    /**
     * The default value of accidental for the default constructor
     */
    private static final Accidental DEFAULT_SUB_NOTE = Accidental.NONE;

    /**
     * The default value of octave for the default constructor
     */
    private static final Octave DEFAULT_OCTAVE = Octave.OneLine;

    /**
     * The note value
     */
    private Note note;

    /**
     * The accidental value
     */
    private Accidental accidental;

    /**
     * The octave value
     */
    private Octave octave;

    /**
     * The default constructor
     */
    public NoteSound() {
        note = DEFAULT_NOTE;
        accidental = DEFAULT_SUB_NOTE;
        octave = DEFAULT_OCTAVE;
    }

    /**
     * The constructor with note and octave.
     * The accidental is had the default value
     * @param note the note value
     * @param octave the octave value
     */
    public NoteSound(Note note, Octave octave) {
        this.note = note;
        this.accidental = DEFAULT_SUB_NOTE;
        this.octave = octave;
    }

    /**
     * The full constructor for the note sound
     *
     * @param note the note value
     * @param accidental the accidental value
     * @param octave the octave value
     */
    public NoteSound(Note note, Accidental accidental, Octave octave) {
        this.note = note;
        this.accidental = accidental;
        this.octave = octave;
    }

    /**
     * The getter for note value.
     *
     * @return the note value
     */
    public Note getNote() {
        return note;
    }

    /**
     * The getter for accidental value.
     *
     * @return the accidental value
     */
    public Accidental getAccidental() {
        return accidental;
    }

    /**
     * The getter for the octave value.
     *
     * @return the octave value
     */
    public Octave getOctave() {
        return octave;
    }

    /**
     * Return true if note is transformable.
     * Example: C# can be transformed to Db. In this case returns true.
     * Example: G note can't be transformed to another note.
     * In this case returns false.
     *
     * @return true is transformable
     */
    public boolean transformable() {
        return accidental != Accidental.NONE;
    }

    /**
     * Octave can't be changed because the smallest and the biggest notes
     * haven't sub notes on border of octaves
     *
     * @return transformed NoteSound or null if it can't be transformed
     */
    public NoteSound transformNote() {
        NoteSound result = null;

        switch (accidental) {
            case FLAT:
                result = new NoteSound(note.getPreviousNote(), Accidental.SHARP, octave);
                break;
            case SHARP:
                result = new NoteSound(note.getNextNote(), Accidental.FLAT, octave);
                break;
        }

        return result;
    }

    @Override
    public int compareTo(NoteSound o) {
        //todo implement the method
        return 0;
    }

    /**
     * Check note and accidental for allowed values
     *
     * @param note for testing
     * @param accidental for testing
     */
    private static void checkNote(Note note, Accidental accidental) {
        if (note == null || accidental == null)
            throw new RuntimeException(new NullPointerException("NoteSound: note is null"));
    }

    /**
     * Checks octave for allowed values.
     * Octave can't be null
     *
     * @param octave for testing
     */
    private static void checkOctave(Octave octave) {
        if (octave == null)
            throw new RuntimeException(new NullPointerException("NoteSound instance: octave reference is null"));
    }
}
